package rps.game;

import java.rmi.RemoteException;

import rps.client.GameController;
import rps.client.GameListener;
import rps.game.data.AttackResult;
import rps.game.data.Figure;
import rps.game.data.FigureKind;
import rps.game.data.Move;
import rps.game.data.Player;

/**
 * The {@code GameImpl} is an implementation for the {@code Game} interface. It
 * contains the necessary logic to play a game.
 */
public class GameImpl implements Game {

	private GameListener	listener1;
	private GameListener	listener2;
	private Player			player1;
	private Player			player2;
	private Player			playerJustMoved;
	private Move			lastMove;
	private Figure[]		assignment;
	private FigureKind		p1Choice;
	private FigureKind		p2Choice;
	private int				update;
	private boolean				surrender;


	public GameImpl(GameListener listener1, GameListener listener2) throws RemoteException {
		this.listener1 = listener1;
		this.listener2 = listener2;
		this.player1 = listener1.getPlayer();
		this.player2 = listener2.getPlayer();
		assignment = new Figure[42];
		surrender = false;
		lastMove = null;
	}

	@Override
	public void sendMessage(Player p, String message) throws RemoteException {
		listener1.chatMessage(p, message);
		listener2.chatMessage(p, message);
		// TODO Auto-generated method stub
	}

	@Override
	public void setInitialAssignment(Player p, FigureKind[] assignment) throws RemoteException {		
		if (p.equals(player1) && checkField(p)) {
			for (int i = 28; i < 42; i++) {
				this.assignment[i] = new Figure (assignment[i], p);
			}
			if (this.lastMove == null) {
				this.lastMove = new Move (0, 0, this.assignment.clone());
			} else {
				listener2.provideInitialChoice();
				listener1.provideInitialChoice();
			}
		} else if (p.equals(player2) && checkField(p)) {
			for (int i = 0; i < 14; i++) {
				this.assignment[i] = new Figure (assignment[i], p);
			}
			if (this.lastMove == null) {
				this.lastMove = new Move (0, 0, this.assignment.clone());
			} else {
				listener1.provideInitialChoice();
				listener2.provideInitialChoice();
			}
		}
	}
	
	private boolean checkField (Player player) throws IllegalStateException {
		if (player.equals(player1)) {
			for (int i = 28; i < 42; i++) {
				if (!(this.assignment[i] == null)) {
					throw new IllegalStateException();
				}
			}
			return true;
			
		} else if (player.equals(player2)) {
			for (int i = 0; i < 13; i++) {
				if (!(this.assignment[i] == null)) {
					throw new IllegalStateException();
				}
				
			}
			return true;
		}
		
		return false;
	}

	@Override
	public void setInitialChoice(Player p, FigureKind kind) throws RemoteException {
		if (p.equals(player1)) {
		//	System.out.println("a");
			this.p1Choice = kind;
		} else if (p.equals(player2)) {
		//	System.out.println("b");
			this.p2Choice = kind;
		}

		if (!((this.p1Choice == null) || (this.p2Choice == null))) {
			if (p1Choice.attack(p2Choice) == AttackResult.DRAW) {
				p1Choice = null;
				p2Choice = null;
				listener1.provideInitialChoice();
				listener2.provideInitialChoice();
			} else if (p1Choice.attack(p2Choice) == AttackResult.WIN) {
				listener1.provideNextMove();
				listener1.startGame();
				listener2.startGame();
				playerJustMoved = player2;
			} else {
				listener1.startGame();
				listener2.startGame();
				listener2.provideNextMove();
				playerJustMoved = player1;
			}
		}
	}
	
	private boolean opponentIsAbleToMove (Player p) {
		for (int i = 0; i < 42; i++) {
			if (this.assignment[i] != null) {
				if (!(this.assignment[i].belongsTo(p))) {
					if (this.assignment[i].getKind() != FigureKind.TRAP && this.assignment[i].getKind() != FigureKind.FLAG)
						if (this.checkCorner(i))
							return true;			
				}
			}
		}
		return false;
	}
	
	private boolean checkCorner (int index) {
		switch (index) {
		case 0:
			if (this.checkTrapOrFlag(1) && this.checkTrapOrFlag(7))
				return false;
			
			break;
		case 6:
			if (this.checkTrapOrFlag(5) && this.checkTrapOrFlag(13))
				return false;
			
			break;
		case 35:
			if (this.checkTrapOrFlag(36) && this.checkTrapOrFlag(28))
				return false;
			break;
		
		case 41:
			if (this.checkTrapOrFlag(40) && this.checkTrapOrFlag(34))
				return false;
			break;
		default: return true;
		}
		return true;
	}
	
	private boolean checkTrapOrFlag (int index) {
		if (this.assignment[index] != null) {
			if (this.assignment[index].getKind() == FigureKind.TRAP || this.assignment[index].getKind() == FigureKind.FLAG)
				return true;
		}
		return false;
	}

	@Override
	public void move(Player movingPlayer, int fromIndex, int toIndex) throws RemoteException, IllegalStateException {
		System.out.println(movingPlayer + ": " + fromIndex + " --> " + toIndex);
		AttackResult attacke = null;
		this.update = 0;
		Figure[] temp = this.assignment.clone();
		// System.out.println(movingPlayer.getNick());
		// System.out.println(this.opponentIsAbleToMove(movingPlayer));
		// System.out.println(movingPlayer == this.playerJustMoved);
		// System.out.println((lastMove.getFrom() != fromIndex && lastMove.getTo() != toIndex));
		if(movingPlayer.getNick().equalsIgnoreCase(playerJustMoved.getNick()) && (lastMove.getFrom() != fromIndex && lastMove.getTo() != toIndex) && this.opponentIsAbleToMove(movingPlayer))
			throw new IllegalStateException();
		
		if (movingPlayer.getNick().equalsIgnoreCase(player1.getNick())) {
			this.playerJustMoved = this.player1;
		} else {
			this.playerJustMoved = this.player2;
		}
		
		this.lastMove = new Move (fromIndex, toIndex, temp);
			
		if (this.assignment[toIndex] == null) {
			
			this.assignment[toIndex] = this.assignment[fromIndex];
			this.assignment[fromIndex] = null;
			
			listener1.figureMoved();
			listener2.figureMoved();
			
		} else {
			attacke = this.assignment[fromIndex].attack(this.assignment[toIndex]);
			this.assignment[toIndex].setDiscovered();
			this.assignment[fromIndex].setDiscovered();
			switch (attacke) {
			case DRAW:
				listener1.provideChoiceAfterFightIsDrawn();
				listener2.provideChoiceAfterFightIsDrawn();
				break;
			case WIN:
				System.out.println(this.assignment[fromIndex].getKind() + " VS " + this.assignment[toIndex].getKind());
				System.out.println(movingPlayer.getNick() + " wins the fight!");
				this.assignment[toIndex] = this.assignment[fromIndex];
				this.assignment[fromIndex] = null;
				temp[toIndex].setDiscovered();
				temp[fromIndex].setDiscovered();
				listener1.figureAttacked();
				listener2.figureAttacked();
				break;
			case LOOSE:
				this.assignment[fromIndex] = null;
				temp[toIndex].setDiscovered();
				temp[fromIndex].setDiscovered();
				listener1.figureAttacked();
				listener2.figureAttacked();
				break;
			case WIN_AGAINST_FLAG:
				if (movingPlayer == this.player1) {
					listener1.gameIsWon();
					listener2.gameIsLost();
				} else {
					listener1.gameIsLost();
					listener2.gameIsWon();
				}
				temp[toIndex].setDiscovered();
				temp[fromIndex].setDiscovered();
				this.uncoverAll();
				break;
			case LOOSE_AGAINST_TRAP:
				this.assignment[fromIndex] = null;
				this.assignment[toIndex] = null;
				if (this.gameDrawn()) {
					listener1.gameIsDrawn();
					listener2.gameIsDrawn();
				}
				break;
			default: break;
			}
		}
		
		if (attacke != AttackResult.DRAW) {
			if(!this.opponentIsAbleToMove(this.player1))
				listener1.provideNextMove();
			else if(!this.opponentIsAbleToMove(this.player2))
				listener2.provideNextMove();
			else if (movingPlayer.equals(this.player1)) {
				listener2.provideNextMove();
			} else {
				listener1.provideNextMove();
			}
		}
		
	}

	private void uncoverAll () {
		for (int i = 0; i < 42; i++) {
			if (this.assignment[i] != null)
				this.assignment[i].setDiscovered();
		}
	}
	private boolean gameDrawn () {
		if (!this.opponentIsAbleToMove(player1) && !this.opponentIsAbleToMove(player2)) {
			return true;
		}
		 return false;
	}
	@Override
	public void setUpdatedKindAfterDraw(Player p, FigureKind kind) throws RemoteException {
		Move tempMove = this.getLastMove();
		this.update ++;
		
		if (p.getNick().equals(playerJustMoved.getNick())) {
			this.assignment[tempMove.getFrom()] = new Figure (kind, p);
		} else {
			this.assignment[tempMove.getTo()] = new Figure (kind, p);
		}
		if (this.update == 2)
			this.move(playerJustMoved, this.getLastMove().getFrom(), this.getLastMove().getTo());
	}

	@Override
	public void surrender(Player p) throws RemoteException, IllegalStateException  {
		if (surrender == false) {
			if (p == this.player1) {
				listener1.gameIsLost();
				listener2.gameIsWon();
				surrender = true;
			} else {
				listener1.gameIsWon();
				listener2.gameIsLost();
				surrender = true;
			}
			listener1.chatMessage(p, "I surrender");
			listener2.chatMessage(p, "I surrender");
		} else {
			throw new IllegalStateException();
		}
	}

	@Override
	public Figure[] getField() throws RemoteException {
		return this.assignment;
	}

	@Override
	public Move getLastMove() throws RemoteException {
			return this.lastMove;
	}

	@Override
	public Player getOpponent(Player p) throws RemoteException {
		if (p.equals(player1)) {
			return this.player2;
		} else {
			return this.player1;
		}
	}
}